package com.anji.hyperneat.onlinereinforcement;

import java.util.ArrayList;

import com.anji.hyperneat.nd.ActivatorNDFacade;
import com.anji.hyperneat.nd.NDFloatArray;

public class RotationalBackPropActivator3d extends ActivatorNDBackPropagator {

	public RotationalBackPropActivator3d(ActivatorNDLR phenotype) {
		super(phenotype);
	}

	@Override
	public void updateNet(NDFloatArray desiredOuts) {
		updateNet(desiredOuts, 1.0f);
	}
	
	@Override
	public void updateNet(NDFloatArray desiredOuts, float learningRateModifier) {
		ArrayList<TrainingPair> pairs = generateTrainingPairs(desiredOuts);
		shufflePairs(pairs);
		
		for (TrainingPair pair : pairs) {
			this.next(pair.input);
			this.performBackPropagation(pair.desiredOutput, learningRateModifier);
		}
	}

	private static void shufflePairs(ArrayList<TrainingPair> pairs) {
		for (int i = 0; i < pairs.size(); i++) {
			int swapIdx = (int) Math.floor(Math.random() * pairs.size());
			TrainingPair temp = pairs.get(swapIdx);
			pairs.set(swapIdx, pairs.get(i));
			pairs.set(i, temp);
		}
	}

	private ArrayList<TrainingPair> generateTrainingPairs(NDFloatArray desiredOuts) {
		ArrayList<TrainingPair> pairs = new ArrayList<TrainingPair>();
		
		TrainingPair pair = new TrainingPair(this.getInputs(), desiredOuts);
		pairs.add(pair);
		
		for (int i = 0; i < 3; i++) {
			pair = new TrainingPair(rotateArray(pair.input), rotateArray(pair.desiredOutput));
			pairs.add(pair);
		}
				
		return pairs;
	}

	private static NDFloatArray rotateArray(NDFloatArray input) {
		int[] dim = input.getDimensions();
		if (dim.length > 3 || dim[0] != dim[1]) 
			throw new IllegalArgumentException("Network inputs must be 3 dimensional and first two dimensions must match.");
		
		NDFloatArray rotated = new NDFloatArray(dim);
    	
		for (int z = 0; z < dim[2]; z++) {
			for (int y = 0; y < dim[1]; y++) {
				for (int x = 0; x < dim[0]; x++) {
					rotated.set(
							input.get(y, dim[1] - x - 1, z)
							, x, y, z);
				}
			}			
		}		
		
		return rotated;
	}
	
	public static void main(String[] args) {
//		NDFloatArray a = new NDFloatArray(3,3,3);
//		
//		float x = 0.0f;
//		for(MatrixIterator it = a.iterator(); it.hasNext(); it.next()) {
//			it.set(x++);
//		}
//		System.out.println(a);
//		
//		
//		NDFloatArray rotated = rotateArray(a);
//		System.out.println(rotated);
		
		
//		ArrayList<TrainingPair> pairs = new ArrayList<TrainingPair>();
//		NDFloatArray a = new NDFloatArray(1, 1);
//		a.set(1.0f, 0,0);
//		NDFloatArray b = new NDFloatArray(1, 1);
//		b.set(2.0f, 0,0);
//		pairs.add(new TrainingPair(a, b));
//		
//		a = new NDFloatArray(1, 1);
//		a.set(3.0f, 0,0);
//		b = new NDFloatArray(1, 1);
//		b.set(4.0f, 0,0);
//		pairs.add(new TrainingPair(a, b));
//		
//		a = new NDFloatArray(1, 1);
//		a.set(5.0f, 0,0);
//		b = new NDFloatArray(1, 1);
//		b.set(6.0f, 0,0);
//		pairs.add(new TrainingPair(a, b));
//		
//		for (TrainingPair pair : pairs) System.out.println(pair.input + "\n" + pair.desiredOutput + "\n**********");
//		
//		shufflePairs(pairs);
//		
//		System.out.println("After: ");
//		for (TrainingPair pair : pairs) System.out.println(pair.input + "\n" + pair.desiredOutput + "\n**********");
			
//		ActivatorNdLrFacade net = new ActivatorNdLrFacade(new int[] {3,3,3}, new int[] {3,1,1});
//		RotationalBackPropActivator3d rbppa = new RotationalBackPropActivator3d(net);
//		
//		NDFloatArray ins = new NDFloatArray(3,3,3);
//		ins.set(.0f, 0,0,0);
//		ins.set(.1f, 1,0,0);
//		ins.set(.2f, 2,0,0);
//		ins.set(.3f, 0,1,0);
//		ins.set(.4f, 1,1,0);
//		ins.set(.5f, 2,1,0);
//		ins.set(.6f, 0,2,0);
//		ins.set(.7f, 1,2,0);
//		ins.set(.8f, 2,2,0);
//		ins.set(.9f, 0,0,1);
//		ins.set(.10f, 1,0,1);
//		ins.set(.11f, 2,0,1);
//		ins.set(.12f, 0,1,1);
//		ins.set(.13f, 1,1,1);
//		ins.set(.14f, 2,1,1);
//		ins.set(.15f, 0,2,1);
//		ins.set(.16f, 1,2,1);
//		ins.set(.17f, 2,2,1);
//		ins.set(.18f, 0,0,2);
//		ins.set(.19f, 1,0,2);
//		ins.set(.20f, 2,0,2);
//		ins.set(.21f, 0,1,2);
//		ins.set(.22f, 1,1,2);
//		ins.set(.23f, 2,1,2);
//		ins.set(.24f, 0,2,2);
//		ins.set(.25f, 1,2,2);
//		ins.set(.26f, 2,2,2);
//		
//		NDFloatArray desiredOuts = new NDFloatArray(3,3,1);
//		desiredOuts.set(.101f, 0,0,0);
//		desiredOuts.set(.102f, 1,0,0);
//		desiredOuts.set(.103f, 2,0,0);
//		desiredOuts.set(.104f, 0,1,0);
//		desiredOuts.set(.105f, 1,1,0);
//		desiredOuts.set(.106f, 2,1,0);
//		desiredOuts.set(.107f, 0,2,0);
//		desiredOuts.set(.108f, 1,2,0);
//		desiredOuts.set(.109f, 2,2,0);
//		
//		rbppa.next(ins);
//		
//		ArrayList<TrainingPair> pairs = rbppa.generateTrainingPairs(desiredOuts);
//		
//		System.out.println(pairs);
	}

}

class TrainingPair {
	public NDFloatArray input;
	public NDFloatArray desiredOutput;
	public TrainingPair(NDFloatArray input, NDFloatArray desiredOutput) {
		this.input = input.clone();
		this.desiredOutput = desiredOutput.clone();
	}
}